iT邦幫忙

2022 iThome 鐵人賽

DAY 14
0
Modern Web

用 Node.js 打造後端 API系列 第 14

Day 14 - JSON Web Token

  • 分享至 

  • xImage
  •  

前言


在開始今天的實作前,我想先帶各位了解JWT(JSON Web Token)是啥,以及它的應用
JWT是一種用於client與server間共享安全資訊的開放標準
每一組JWT token包含了header(演算法&token的種類), payload(資料)與verify signature(確保資訊在傳輸時沒有改變)

實作JWT驗證


首先需安裝jsonwebtoken
當使用者註冊或登入系統時,我們要透過cookie回傳一組jwt token給他們
所以也需安裝cookie-parser
由於jwt token是在使用者註冊或登入才被創造出來的,所以要用mongoose method去操作user(document)

UserSchema.methods.getSignedJwtToken = function() {
  return jwt.sign({ id: this._id }, process.env.JWT_SECRET, {
    expiresIn: process.env.JWT_EXPIRE
  });
}

JWT_SECRET或JWT_EXPIRE都可以自己設定

完成jwt token的設定後,我們來想想該怎麼將token回傳給user

const sendTokenResponse = (user, statusCode, res) => {
  // Create token
  const token = user.getSignedJwtToken();

  const options = {
    expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
    httpOnly: true
  }

  res
    .status(statusCode)
    .cookie('token', token, options)
    .json({
      success: true,
      token
    });
}

回傳給使用者的cookie須包含:名稱、token與options
此處的options設置了cookie的到期日為30天&禁止javascript直接存取cookie(httpOnly)

註冊&登入


由於多了回傳jwt token這項功能,我們要將昨天register controller的回傳值改為

sendTokenResponse(user, 200, res);

在登入的部分,就變得有一點點複雜
我們要處理的情況有:

  • 未輸入email或password, return error
  • 在db中找不到user(輸入的email錯誤), return error
  • 輸入的密碼錯誤, return error

// 第一種情況

// 先destructure object
const { email, password } = req.body;

if (!email || !password) {
  return next(new ErrorResponse('Please provide an email and password', 400));
}

// 第二種情況
在寫user的schema時,為了避免搜尋結果return password,password的select為false
但我們等等在驗證第三種情況時,需要驗證輸入的密碼是否正確,所以在此處要回傳password field

const user = await User.findOne({ email }).select('+password');

if (!user) {
  return next(new ErrorResponse('Invalid credentials', 401));
}

// 第三種情況

const isMatch = await user.matchPassword(password);

if (!isMatch) {
  return next(new ErrorResponse('Invalid credentials', 401));
}

還記得使用者在註冊時,我們有將他們輸入的密碼用bcryptjs加密後,才存入db對吧
但要如何將加密後的密碼與user login輸入的密碼作比較呢?
可以用bcrypt.compare來比較!
一樣用mongoose method去操作user(document)

UserSchema.methods.matchPassword = async function(enteredPassword) {
  return await bcrypt.compare(enteredPassword, this.password);
}

上一篇
Day 13 - 建立User Model
下一篇
Day 15 - Route保護機制
系列文
用 Node.js 打造後端 API30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言